package com.tinyspring.factory;

import com.tinyspring.advisor.AdviceType;
import com.tinyspring.advisor.Advisor;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.List;

/**
 * @author: TianYuan
 * @description: 代理创建工厂
 * @version: 1.0
 */
public class AopProxyFactory {

    /**
     * 为bean创建代理对象
     * @param target
     * @param matchedAdvisors
     * @return
     */
    public static Object createProxy(Object target, List<Advisor> matchedAdvisors){
        // 如果有接口使用JDK动态代理，否则使用CGLIB
        if (target.getClass().getInterfaces().length > 0){
            return createJdkDynamicProxy(target, matchedAdvisors);
        }else {
            System.out.println("暂未实现使用CGLIB创建代理对象");
            return null;
        }
    }

    private static Object createJdkDynamicProxy(final Object target, final List<Advisor> advisors){
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        return executeWithAdvice(target, method, args, advisors);
                    }
                }
        );
    }

    /**
     * 执行通知
     * @param target
     * @param method
     * @param args
     * @param advisors
     * @return
     * @throws Throwable
     */
    private static Object executeWithAdvice(Object target, Method method, Object[] args,
                                            List<Advisor> advisors) throws Throwable {
        //执行前置通知
        for (Advisor advisor:advisors){
            if (advisor.getAdviceType() == AdviceType.BEFORE && advisor.getPointcutExpression().equals(target.getClass().getName()+"."+method.getName()+"()")){
                advisor.getAdviceMethod().invoke(advisor.getAspectBean(),args);
            }
        }
        //执行目标方法
        Object result = method.invoke(target, args);
        //执行后置通知
        for (Advisor advisor : advisors) {
            if (advisor.getAdviceType() == AdviceType.AFTER && advisor.getPointcutExpression().equals(target.getClass().getName()+"."+method.getName()+"()")){
                advisor.getAdviceMethod().invoke(advisor.getAspectBean());
            }
        }
        return result;
    }
}
